/*
    通过system V共享内存的方法实现两个不同的进程进行通信
*/
#pragma once
#ifndef __COMMAND__SHM
#define __COMMAND__SHM
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/stat.h>
using namespace std;
#define Pathname "."  
#define SIZE 4096    //共享内存的大小，字节单位
static int Proj_id = 0x666;
#define NUM 1024   //缓冲区大小
// 获取key值，为创建共享内存做铺垫
key_t getKey()
{
    key_t k = ftok(Pathname, Proj_id);
    if (k == -1)
    {
        cerr << "ftok error:" << errno << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

// 将key值转化为十六进制数
string toHex(key_t k)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "%x", k);
    string s = buffer;
    return s;
}

// 创建共享内存
// int shmget(key_t key, size_t size, int shmflg);
// IPC_CREAT: 如果共享内存不存在，则创建一个新的。如果已存在，忽略这个标志。
// IPC_EXCL: 与 IPC_CREAT 同时使用，确保创建一个新的共享内存段。如果已存在，返回错误。
int commonShm(key_t key, size_t size, int flag)
{
    int shm_id = shmget(key, size, flag);
    if (shm_id == -1)
    {
        cerr << "shmget error:" << errno << strerror(errno) << endl;
        exit(2);
    }
    return shm_id;
}
int creatShm(key_t key, size_t size)
{
    umask(0);
    return commonShm(key, size, IPC_CREAT | IPC_EXCL | 0666);
}
int getShm(key_t key, size_t size)
{
    return commonShm(key, size, IPC_CREAT);
}

//释放共享内存
// int shmctl(int shmid, int cmd, struct shmid_ds *buf);
void delShm(int shm_id)
{
    int fg = shmctl(shm_id, IPC_RMID, nullptr);
    if (fg == -1)
    {
        cerr << "shmctl error:" << errno << strerror(errno) << endl;
        exit(3);
    }
}

//关联共享内存
//void *shmat(int shmid, const void *shmaddr, int shmflg);
char* attchShm(int shm_id)
{
    char* shm_address_start = (char*)shmat(shm_id,nullptr,0);
    if(shm_address_start == (char*) -1)
    {
        cerr << "shmat error:" << errno << strerror(errno) << endl;
        exit(4);
    }
    return shm_address_start;
}

//取消关联共享内存
//int shmdt(const void *shmaddr);
void unlinkShm(char* shm_address)
{
     int n = shmdt(shm_address);
     if(n == -1)
     {
        cerr << "shmdt error:" << errno << strerror(errno) << endl;
        exit(5);
     }
}
#define CLIENT 0
#define SERVER 1
class Init
{
public:
//构造函数：1.创建共享内存空间 2.进程与共享内存建立关联

Init(int t):type(t)
{
    cout<<"进程："<<type<<endl;
    //1.创建共享内存空间
    int k = getKey();
    if(type == SERVER)
    {//SERVER创建共享内存
       cout<<"SERVER创建共享内存"<<endl;
       shm_id = creatShm(k,SIZE);
       cout<<"shm_id:"<<shm_id<<endl;
    }else if(type == CLIENT)
    {//CLIENT获取共享内存
     cout<<"CLIENT获取共享内存"<<endl;
        shm_id = getShm(k,SIZE);
     cout<<"shm_id:"<<shm_id<<endl;
    }
    cout<<"进程与共享内存建立关联"<<endl;
    //2.进程与共享内存建立关联
    start = attchShm(shm_id);
   cout<<"进程与共享内存建立关联成功"<<endl;
}
char* Start()
{
  return start;
}

//析构函数：1.进程与共享内存取消关联 2.释放共享内存空间
~Init()
{
    unlinkShm(start);  //取消关联
    if(type ==SERVER )  //释放释放共享内存
     delShm(shm_id);
}

private:
int type;  //不同的进程
char* start; //共享内存空间的首地址
int shm_id;  
};

#endif